package com.rest.impl;

import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.InputStreamBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import com.rest.model.HttpMethod;
import com.rest.model.Parameter;
import com.rest.model.RestRequest;
import com.rest.model.RestResponse;

public class RestMultipartRequest implements RestRequest {

	private static final HttpClient httpclient = new DefaultHttpClient();

	private final HttpEntityEnclosingRequestBase req;

	private final String url;
	private final Map<String, String> headers;
	private final List<Parameter<String>> queryParams;
	private final List<Parameter<Object>> formParams;

	RestMultipartRequest(String url, Map<String, String> headers, List<Parameter<String>> queryParams, List<Parameter<Object>> formParams) {
		this.url = url;
		this.headers = new HashMap<String, String>(headers);
		this.queryParams = Collections.unmodifiableList(queryParams);
		this.formParams = Collections.unmodifiableList(formParams);

		List<NameValuePair> qparams = new ArrayList<NameValuePair>();

		for (Parameter<String> param : queryParams) {
			qparams.add(new BasicNameValuePair(param.getName(), param.getValue()));
		}
		URI uri = URI.create(url + "?" + URLEncodedUtils.format(qparams, "UTF-8"));

		this.req = new HttpPost(uri);

		for (Entry<String, String> headerEntry : headers.entrySet()) {
			req.addHeader(headerEntry.getKey(), headerEntry.getValue());
		}
		try {

			MultipartEntity reqEntity = new MultipartEntity();

			Map<String, InputStreamBody> fileFormParams = new HashMap<String, InputStreamBody>();

			for (Parameter<Object> param : formParams) {

				Class<?> formParamClass = param.getValue().getClass();
				if (FileInputStream.class.isAssignableFrom(formParamClass)) {
					FileInputStream fileInputStream = (FileInputStream) param.getValue();
					InputStreamBody uploadFilePart = new InputStreamBody(fileInputStream, "file");
					fileFormParams.put(param.getName(), uploadFilePart);
				} else {
					String textValue = String.valueOf(param.getValue());
					StringBody str = new StringBody(textValue);
					reqEntity.addPart(param.getName(), str);
				}
			}
			for (Map.Entry<String, InputStreamBody> fileFormParam : fileFormParams.entrySet()) {
				reqEntity.addPart(fileFormParam.getKey(), fileFormParam.getValue());
			}
			req.setEntity(reqEntity);
		} catch (Exception e) {
			throw new IllegalStateException("Problems creating multipart request", e);
		}
	}

	@Override
	public RestResponse send() {
		try {
			RequestLogger.logRequest(this);
			HttpResponse response = httpclient.execute(req);
			InputStream content = response.getEntity().getContent();

			int statusCode = response.getStatusLine().getStatusCode();
			String body = EntityUtils.toString(response.getEntity());

			RestResponse apiResponse = new RestResponseImpl(this, statusCode, body, content);

			RequestLogger.logResponse(apiResponse);
			return apiResponse;

		} catch (Exception e) {
			throw new IllegalStateException("Problems sending multipart request", e);
		}
	}

	@Override
	public HttpMethod getMethod() {
		return HttpMethod.POST;
	}

	@Override
	public String getUrl() {
		return url;
	}

	@Override
	public String getHeader(String name) {
		if (!headers.containsKey(name)) {
			throw new IllegalArgumentException(String.format("Header '%s' doesn't exist in request", name));
		}
		return headers.get(name);
	}

	@Override
	public Map<String, String> getHeaders() {
		return Collections.unmodifiableMap(headers);
	}

	@Override
	public List<Parameter<String>> getQueryParams() {
		return queryParams;
	}

	@Override
	public List<Parameter<Object>> getFormParams() {
		return formParams;
	}
}
